-module(toplevel).
-include_lib ("nitrogen/include/wf.inc").
-include ("useful.hrl").
-compile(export_all).

-define(TIMEOUT, 600). %10 minutes

start_loop() ->
    spawn(toplevel, loop, [dict:new(), dict:new()]).

start_cleanup() ->
    spawn(toplevel, cleanup, []).

cleanup() ->
    receive
	_ -> cleanup()
    after (?TIMEOUT * 1000) ->
	    hertz_top ! cleanup,
	    cleanup()
    end.

touch(Dict, ID) ->
    dict:append(ID, now(), dict:erase(ID, Dict)).

loop(Ports, Log) ->
    receive
	cleanup ->
	    {UpdatedPorts, UpdatedLog} = do_cleanup(Ports, Log),
	    loop(UpdatedPorts, UpdatedLog);
	{Command, ID, Callback, Args} ->
	    UpdatedLog = touch(Log, ID),
	    UpdatedPorts = case dict:find(ID, Ports) of
			       {ok, [Port | _]} ->
				   {NPorts, Message} = ok_act(Command, {Ports, ID, Port, Args}),
				   Callback ! Message,
				   NPorts;
			       error ->
				   {NPorts, Message} = error_act(Command, {Ports, ID, Args}),
				   Callback ! Message,
				   NPorts
			   end,
	    loop(UpdatedPorts, UpdatedLog)
    end.

ok_act(Command, {Ports, ID, Port, Args}) ->
    case Command of
	talk ->
	    {Message} = Args,
	    {Ports, {ok, talk:say(Port, Message)}}; 
	add ->
	    {Ports, {error, already_connected}};
	remove ->
	    talk:bye(Port),
	    { dict:erase(ID, Ports) , ok }
    end.

error_act(Command, {Ports, ID, Args}) ->
    case Command of
	talk ->
	    {Ports, error};
	add ->
	    { dict:append(ID, talk:hi(?HERTZ_TOPLEVEL_PATH), Ports), ok };
	remove ->
	    { Ports, {error, not_connected}}
    end.

do_cleanup(Ports, Log) ->
    CloseList = dict:fold( fun(K, [V | _], Acc) ->
				   {_, Time} = calendar:time_difference(calendar:now_to_datetime(V), calendar:now_to_datetime(now())),	   
				   Sec = calendar:time_to_seconds(Time),
				   if
				       Sec >= ?TIMEOUT ->
					   Acc ++ [K];
				       true ->
					   Acc
				   end
			   end, [], Log),

    lists:map( fun(X) ->
		       case dict:find(X, Ports) of
			   {ok, [Port | _]} ->
			       talk:bye(Port);
			   _ -> ok
		       end
	       end, CloseList ),
    [NewPorts, NewLog] = lists:map( fun(X) ->
					    dict:filter( fun(K, _) -> not (lists:member(K, CloseList)) end, X)
				    end, [Ports, Log]),
    {NewPorts, NewLog}.

%% loop(Dict, InUse) ->
%%     receive 
%% 	{talk, From, ID, Message} ->
%% 	    NInUse = touch(InUse, ID),
%% 	    case dict:find(ID, Dict) of
%% 		{ok, [Port | _]} -> 
%% 		    From ! {ok, talk:say(Port, Message)},
%% 						%		    io:format("~w~n", [talk:say(Port, Message)]),
%% 		    loop(Dict, NInUse);
%% 		error -> 
%% 		    From ! error,
%% 		    loop(Dict, NInUse)
%% 	    end;
%% 	{add, From, ID} ->
%% 	    NInUse =  touch(InUse, ID),
%% 	    case dict:find(ID, Dict) of
%% 		{ok, _} -> 
%% 		    From ! {error, already_connected},
%% 		    loop(Dict, NInUse);
%% 		error -> 
%% 		    From ! ok,
%% 		    loop(dict:append(ID, talk:hi(?HERTZ_TOPLEVEL_PATH), Dict), NInUse)
%% 	    end;
%% 	{remove, From, ID} ->
%% 	    NInUse = touch(InUse, ID),
%% 	    case dict:find(ID, Dict) of
%% 		{ok, [Port | _]} ->
%% 		    talk:bye(Port),
%% 		    From ! ok,
%% 		    loop(dict:erase(ID,  Dict), NInUse);
%% 		_ ->
%% 		    From ! {error, not_connected},
%% 		    loop(Dict, NInUse)
%% 	    end;
%% 	{cleanup} ->
%% 	    io:format("Cleanup process...~n"),
%% 	    ToErase = dict:fold( fun(K, [V | _], Acc) ->
%% 						% io:format("V : ~w~n", [V]),
%% 					 {_, Time} = calendar:time_difference(calendar:now_to_datetime(V), calendar:now_to_datetime(now())),
%% 						% io:format("Time : ~w~n", [Time]),
%% 					 Sec = calendar:time_to_seconds(Time),
%% 					 if
%% 					     Sec >= ?TIMEOUT ->
%% 						 Acc ++ [K];
%% 					     true ->
%% 						 Acc
%% 					 end
%% 				 end,
%% 				 [],
%% 				 InUse),
%% 						% io:format("Closing : ~w~n", [ToErase]),
%% 	    lists:map( fun(X) ->
%% 			       case dict:find(X, Dict) of
%% 				   {ok, [Port | _]} ->
%% 				       talk:bye(Port);
%% 				   _ -> ok
%% 			       end
%% 		       end, ToErase ),
%% 	    OutDict = dict:filter( fun(K, _) -> not (lists:member(K, ToErase)) end, Dict ),
%% 	    OutInUse = dict:filter( fun(K, _) -> not (lists:member(K, ToErase)) end , InUse ),
%% 						% io:format("InUse : ~w~n OutInUse : ~w~n", [Dict, OutDict]),
%% 	    loop(OutDict, OutInUse)
%%     end.
